﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

using SharedGenomics.Core;
using SharedGenomics.Workbench.Core;

namespace SharedGenomics.Workbench.Views.WindowsForms
{
    public class WindowsFormsViewManager : IViewManager<Control>
    {
        private string _mainRegionName;
        private IDictionary<string, Control> _regions;

        public WindowsFormsViewManager()
        {
            this._regions = new Dictionary<string, Control>();
        }

        private Control ConstructView(IPresenter presenter)
        {
            //get the view for the presenter and find all the regions on it
            Control parentView = (Control)presenter.View;

            IDictionary<string, Control> viewRegions = this.GetRegionItems<Control>(parentView);

            if (viewRegions.Count > 0)
            {
                //get all the properties on the presenter marked with [Region] attributes - these should all expose presenters
                IDictionary<string, IPresenter> regionedSubPresenters = this.GetRegionItems<IPresenter>(presenter);

                //get the views for each 'sub-presenter' on the input presenter which corresponds to a region on the parent view (i.e. the view associated with the 
                //input presenter)
                foreach (var kvp in regionedSubPresenters)
                {
                    string currentRegionName = kvp.Key;

                    if (viewRegions.ContainsKey(currentRegionName))
                    {
                        //register for help requested events on the current presenter's view
                        kvp.Value.View.HelpRequired += new EventHandler<HelpRequiredEventArgs>(View_HelpRequired);

                        //get the (possibly nested) view for the presenter associated with the current view region
                        Control nestedView = this.ConstructView(kvp.Value);
                        this.SetView(nestedView, viewRegions[currentRegionName]);
                    }
                }
            }

            return parentView;
        }

        private void View_HelpRequired(object sender, HelpRequiredEventArgs e)
        {
            //propagate help request to any listeners
            this.HelpRequested.Raise(this, e);
        }

        private IDictionary<string, T> GetRegionItems<T>(object parent)
        {
            Dictionary<string, T> regions = new Dictionary<string, T>();

            var regs = from p in parent.GetType().GetProperties()
                       let atts = p.GetCustomAttributes(typeof(RegionAttribute), false)
                       where atts.Length == 1
                       select new { RegionName = ((RegionAttribute)atts[0]).Name, Value = (T)p.GetValue(parent, null) };

            foreach (var r in regs)
            {
                if (!regions.ContainsKey(r.RegionName))
                    regions[r.RegionName] = r.Value;
            }

            return regions;
        }

        private void SetView(Control view, Control container)
        {
            container.Controls.Clear();
            container.Controls.Add(view);
            view.Dock = DockStyle.Fill;
        }

        private void ShowDialog(IPresenter presenter, bool modal)
        {
            Form f = new Form();
            Control view = this.ConstructView(presenter);

            f.ClientSize = view.Size;           //size dialog to show the whole form
            f.Controls.Add(view);
            view.Dock = DockStyle.Fill;
            presenter.RequestingClose += (o, e) => { f.Close(); };

            if (modal)
                f.ShowDialog();
            else
                f.Show();
        }

        private Control MainRegion
        {
            get { return this._regions[this._mainRegionName]; }
        }

        #region IViewManager<Control> Members

        public void SetMainForm(Control form, string mainRegionName)
        {
            //find all regions on the input form - these are properties annotated with the 'Region' attribute
            var regions = from p in form.GetType().GetProperties()
                          let atts = p.GetCustomAttributes(typeof(RegionAttribute), false)
                          where atts.Length == 1
                          select new { PropInfo = p, Att = (RegionAttribute)atts[0] };

            //check one of the regions is tagged as the main region
            if (regions.Where(r => r.Att.Name == mainRegionName).Count() == 1)
            {
                //clear old regions and add new ones
                this._regions.Clear();

                foreach (var r in regions)
                {
                    this._regions.Add(r.Att.Name, (Control)r.PropInfo.GetValue(form, null));
                }

                this._mainRegionName = mainRegionName;
            }
        }

        #endregion

        #region IViewDisplayer Members

        public void Show(IPresenter presenter)
        {
            //get view for presenter and display in the main form main region
            //Control view = this.ConstructView(presenter);
            //this.SetView(view, this.MainRegion);
            this.Show(presenter, FormDisplayMode.InPlace);
        }

        public void Show(IPresenter presenter, FormDisplayMode displayMode)
        {
            //if (displayMode == FormDisplayMode.InPlace)
            //{
            //    Control view = this.ConstructView(presenter);
            //    this.SetView(view, this.MainRegion);
            //}
            //else
            //{
            //    this.ShowDialog(presenter, displayMode == FormDisplayMode.ModalDialog);
            //}
        }

        public void ShowModal(IPresenter presenter)
        {
            this.Show(presenter, FormDisplayMode.ModalDialog);
        }

        public IMenu MenuView
        {
            set
            {
                this.SetView((Control)value, this._regions["MainMenu"]);
            }
        }

        public IMenu MenuBar
        {
            set { throw new NotImplementedException(); }
        }

        public event EventHandler<HelpRequiredEventArgs> HelpRequested;

        #endregion
    }
}
